package org.wbd.pklib.db.logic;

import java.sql.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.hibernate.*;
import org.wbd.pklib.db.Book;
import org.wbd.pklib.db.DataBaseSession;
import org.wbd.pklib.db.Item;
import org.wbd.pklib.db.Loan;
import org.wbd.pklib.db.Repo;
import org.wbd.pklib.db.RepoUser;
import org.wbd.pklib.db.User;

/**
 * Singleton obslugujacy operacje na bazie danych zwiazane z obsluga repozytorium
 */
public class RepoLogic {
	private SessionFactory sessionFactory;
	static private RepoLogic repoLogic = null;

	private RepoLogic() {
		sessionFactory = DataBaseSession.getInstance().getSessionFactory();

	};

	static public RepoLogic getInstance() {
		if (repoLogic == null) {
			repoLogic = new RepoLogic();
		}
		return repoLogic;
	}

	/**
	 *Zwraca instancje RepoLogic
	 */
	public LogicRespond createRepo(User user, String name) {
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();

			// creating newRepo
			Repo newRepo = new Repo(null, name);
			session.save(newRepo);

			// connecting user with newRepo
			RepoUser newRepoUser = new RepoUser();
			newRepoUser.setUser(user);
			newRepoUser.setRepo(newRepo);
			newRepoUser.setFunction("admin");
			user.getRepoUsers().add(newRepoUser);

			session.update(user);

			session.getTransaction().commit();
			session.close();
			return new LogicRespond("Utworzono repozytorium", true);
		} catch (Exception e) {
			return new LogicRespond(
					"Takie repozytorium juz istnieje", false);
		}
	}

	/**
	 * Dolacza uzytkownika do danego repozytorium
	 */
	public LogicRespond joinRepo(User user, Repo repo) {

		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();

			// connecting user with newRepo
			RepoUser newRepoUser = new RepoUser();
			newRepoUser.setUser(user);
			newRepoUser.setRepo(repo);
			newRepoUser.setFunction("member");
			user.getRepoUsers().add(newRepoUser);

			session.update(user);
			session.getTransaction().commit();
			session.close();
			return new LogicRespond("Dolaczono do repozytorium", true);
		} catch (Exception e) {
			return new LogicRespond("User not added- entry already exist",
					false);
		}
	}

	/**
	 * Dolacza dana ksiazke do repozytorium, tworzac egzemplarz ktorego wlascicielem jest dany uzytkownik
	 */
	public LogicRespond joinBook(User user, Repo repo, Book book) {

		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();

			// connecting user with newRepo
			Item newItem = new Item();
			newItem.setBook(book);
			newItem.setRepo(repo);
			newItem.setUser(user);
			
			newItem.setItemId(null);
			repo.getItems().add(newItem);
			user.getItems().add(newItem);
			book.getItems().add(newItem);

			session.update(repo);
			
		
			session.getTransaction().commit();
			session.close();
			return new LogicRespond("Dolaczono ksiazke", true);
		} catch (Exception e) {
			return new LogicRespond("Book not attached - entry already exist",
					false);
		}
	}

	/**
	 * Zwraca egzemplarze ksiazek znajdujacie sie w danym repozytorium
	 */
	public LogicRespond getRepoItems(Repo repo) {

		List<Object> itemList = new LinkedList<Object>(repo.getItems());

		LogicRespond logicRespond = new LogicRespond("repo item list", true);
		logicRespond.setResultList(itemList);
		return logicRespond;
	}

	/**
	 * Zwraca egzemplarze ksiazek znajdujacie sie w danym repozytoriumg
	 * gotowe do natychmiastowego wypozyczenia
	 */
	public LogicRespond getFreeBooks(Repo repo) {

		LogicRespond logicRespond;
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();

			List<Object> unfinishedLoans = new LinkedList<Object>();

			Iterator<Item> itemIt = repo.getItems().iterator();
			while (itemIt.hasNext()) {
				unfinishedLoans.addAll(session.createQuery(
						"from Loan where itemId =" + itemIt.next().getItemId()
								+ " " + "and returnDate is null").list());
			}

			List<Object> loanedItems = new LinkedList<Object>();
			Iterator<Object> ulIt = unfinishedLoans.iterator();
			while (ulIt.hasNext()) {
				loanedItems.addAll(session.createQuery(
						"from Item where itemId ="
								+ ((Loan) ulIt.next()).getItemId()).list());
			}

			List<Object> result = getRepoItems(repo).getResultList();
			result.removeAll(loanedItems);

			logicRespond = new LogicRespond("free books", true);
			logicRespond.setResultList(result);
			session.getTransaction().commit();
			session.close();
		} catch (Exception e) {
			logicRespond = new LogicRespond("DB error", false);
		}
		return logicRespond;
	}

	/**
	 * Zwraca ksiazki zwiazane z danym repozytorium
	 */
	public LogicRespond getRepoBooks(Repo repo) {

		LogicRespond logicRespond;

	

			Set<Book> repoBooks = new HashSet<Book>();
			for (Item i : repo.getItems()) {
				repoBooks.add(i.getBook());				
			}

			
			logicRespond = new LogicRespond("DB error", true);
			logicRespond.setResultList((List<Object>) new LinkedList<Object>(
					repoBooks));

	

		return logicRespond;
	}

	/**
	 * Zwraca wszystkie wypozyczenia zwiazane z danym repo
	 */
	public LogicRespond getRepoLoans(Repo repo) {

		LogicRespond logicRespond;
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();
			List<Object> result = new LinkedList<Object>();

			Iterator<Item> itemIt = repo.getItems().iterator();
			while (itemIt.hasNext()) {
				result.addAll(session.createQuery(
						"from Loan where itemId =" + itemIt.next().getItemId())
						.list());
			}

			logicRespond = new LogicRespond("Loan List", true);
			logicRespond.setResultList(result);

			session.getTransaction().commit();
			session.close();
		} catch (Exception e) {
			logicRespond = new LogicRespond("DB error", false);
		}
		return logicRespond;
	}

	/**
	 * Zwraca liste uzytkownikow zwiazanych z danym repozytorium
	 */
	public LogicRespond getRepoUsers(Repo repo) {

		LogicRespond logicRespond = new LogicRespond("Users list", true);
		List<Object> usersList = new LinkedList<Object>();
		for (RepoUser ru : repo.getRepoUsers()) {
			usersList.add(ru.getUser());
		}
		logicRespond.setResultList(usersList);
		return logicRespond;
	}

	/**
	 * Zwraca obiekt klasy Repo zwiazany z danym id
	 */
	public LogicRespond getRepo(Integer repoId) {
		LogicRespond logicRespond;
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();

			Repo repo = (Repo) session.get(Repo.class, repoId);
			repo.getRepoUsers().toString();
			repo.getItems().toString();

			session.getTransaction().commit();
			session.close();
			logicRespond = new LogicRespond("Repo", true, repo);
		} catch (Exception e) {
			logicRespond = new LogicRespond("No repos with specified id", false);
		}
		return logicRespond;
	}
/*
	public LogicRespond getAllRepos() {
		LogicRespond logicRespond;
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();
			List<Object> result = session.createQuery("from Repo").list();
			logicRespond = new LogicRespond("Repo", true);
			logicRespond.setResultList(result);

		} catch (Exception e) {
			logicRespond = new LogicRespond("DB error", false);
		}

		return logicRespond;
	}
	*/
	/**
	 * Zwraca repozytoria do ktorych moze dolaczyc dany uzytkownik
	 */
	public LogicRespond getAvailableRepos(User user) {
		LogicRespond logicRespond;
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();
			List<Object> result = session.createQuery("from Repo where repoId not in (select ru.repoUserJoin.repo.repoId from RepoUser ru where userId="+
					+user.getUserId()+")").list();
			session.getTransaction().commit();
			session.close();
			logicRespond = new LogicRespond("Repo", true);
			logicRespond.setResultList(result);

		} catch (Exception e) {
			logicRespond = new LogicRespond("DB error", false);
		}
		return logicRespond;

	}

	/**
	 * Zwraca egzemplarze zwiazane z danym id
	 */
	public LogicRespond getItem(Integer itemId) {
		LogicRespond logicRespond;
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();

			List<Object> result = session.createQuery(
					"from Item where itemId = " + itemId).list();
			logicRespond = new LogicRespond("True", false, (Item) result.get(0));

			session.getTransaction().commit();
			session.close();
		} catch (Exception e) {
			logicRespond = new LogicRespond("DB error", false);
		}
		return logicRespond;
	}
	/**
	 * Zwraca ksiazki wyszukane wedlug wzorca zawartego w obiekcie book
	 * posrod ksiazek polaczonych z danym repozytorium
	 */
	public LogicRespond searchBooks(Book book, Integer repoId) {

		String bookQuery;

		if ((book.getPublicationYear() != null)
				&& (book.getPublicationYear() != 0)) {
			bookQuery = String
					.format("from Book where isbn like '%%%s%%' and author like '%%%s%%' and title like '%%%s%%' and publicationYear = %d and  publisher like '%%%s%%' and categories like '%%%s%%' ",
							book.getIsbn(), book.getAuthor(), book.getTitle(),
							book.getPublicationYear(), book.getPublisher(), book.getCategories());
		} else {
			bookQuery = String
					.format("from Book where isbn like '%%%s%%' and author like '%%%s%%' and title like '%%%s%%'  and  publisher like '%%%s%%' and categories like '%%%s%%' ",
							book.getIsbn(), book.getAuthor(), book.getTitle(),
							book.getPublisher(), book.getCategories());
		}

		
		
		LogicRespond logicRespond;
		try {
			Session session = sessionFactory.openSession();
			session.beginTransaction();

			List<Object> bookQueryResult = session.createQuery(bookQuery).list();
			List<Object> itemList = new LinkedList<Object>();
			for (Object b : bookQueryResult) {
				System.out.println(((Book)b).getTitle());
				String itemQuery = String.format(
						"from Item where repoId = %d and isbn = '%s' ", repoId,
						((Book) b).getIsbn());
				itemList.addAll(session.createQuery(itemQuery).list());
			}

			for (Object i : itemList) {
				((Item) i).getBook().toString();
				((Item) i).getUser().toString();
			}

			logicRespond = new LogicRespond("Found items", false);
			logicRespond.setResultList(itemList);

			session.getTransaction().commit();
			session.close();
		} catch (Exception e) {
			logicRespond = new LogicRespond("DB error", false);
		}

		return logicRespond;
	}
	
	

}